(function ($) {
    const AppUtils = {
    // 预编译正则（提升性能）
    FORMAT_REGEX : /YYYY|YY|MM|DD|HH|hh|mm|ss|SSS|A|a/g,

    // 日期格式常量
    DATE_FORMATS: {
        DEFAULT: 'YYYY-MM-DD HH:mm:ss',
        DATE: 'YYYYMMDDHHmmss',
        DATE_ONLY: 'YYYY-MM-DD',
        TIME_ONLY: 'HH:mm:ss',
        HUMAN: 'MMM DD, YYYY'
    },

    // 预定义替换器（避免重复创建）
    /**
     * 日期格式化替换器，
     * @param {Date} date - 日期对象
     * @returns {object} 替换对象
     */
    formatReplacer (date) {
        const pad = num => num.toString().padStart(2, '0');
        const replacements = {
            'YYYY': date.getFullYear(),
            'YY': date.getFullYear().toString().slice(-2),
            'MM': pad(date.getMonth() + 1),
            'DD': pad(date.getDate()),
            'HH': pad(date.getHours()),
            'hh': pad(date.getHours() % 12 || 12),
            'mm': pad(date.getMinutes()),
            'ss': pad(date.getSeconds()),
            'SSS': pad(date.getMilliseconds()),
            'A': date.getHours() < 12 ? 'AM' : 'PM',
            'a': date.getHours() < 12 ? 'am' : 'pm'
        };
        // 返回替换函数而不是对象
        return match => replacements[match];
    },

    /**
     * 格式化日期时间
     * @param {Date|number|string} date - 日期对象/时间戳/日期字符串
     * @param {string} format - 格式字符串，默认 YYYY-MM-DD HH:mm:ss
     * @returns {string} 格式化后的日期字符串
     * @example
     * formatDateTime(1672531200000); // "2023-01-01 00:00:00"
     * formatDateTime('2023-01-01', 'YYYY/MM/DD'); // "2023/01/01"
     */
    formatDateTime(date = new Date(), format = AppUtils.DATE_FORMATS.DEFAULT) {
        // 参数校验和转换
        const dateObj = date instanceof Date ? date : new Date(date);
        if (Number.isNaN(dateObj.getTime())) {
            console.warn('Invalid date parameter');
            return 'Invalid Date';
        }

        return format.replace(
            AppUtils.FORMAT_REGEX,
            AppUtils.formatReplacer(dateObj)
        );
    },

    // 防抖函数
    debounce(fn, delay){
        let timer;
        return function (...args) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(this, args);
            }, delay);
        };
    },

    // 节流函数（用于搜索输入框防抖）
    throttle(func, delay) {
        let timer = null;
        return function (...args) {
            if (!timer) {
                timer = setTimeout(() => {
                    func.apply(this, args);
                    timer = null;
                }, delay);
            }
        };
    },

    /*
     * 获取元素距离body顶部的距离
     * @param {HTMLElement} element - 要获取距离的元素
     * @return {number} 元素距离body顶部的距离
     */

    getOffsetTopFromBody($element) {
        if (!$element || !$element.length) return 0;

        const rect = $element[0].getBoundingClientRect();
        return rect.top + window.scrollY;
    },

    /**
     * 验证URL格式
     * @param {string} url - 要验证的URL
     * @return {boolean} 如果URL格式有效则返回true，否则返回false
     */
    isValidUrl(url) {
        try {
            const parsedUrl = new URL(url);
            return !!parsedUrl; // 检查是否存在有效URL对象
        } catch {
            return false;
        }
    },


    /**
     * 检查图片是否存在
     * @param {string} url - 要检查的图片URL
     * @return {Promise<boolean>} 如果图片存在则返回true，否则返回false
     */
    checkImageExists(url) {
        return new Promise(resolve => {
            const img = new Image();
            img.onload = () => resolve(true);
            img.onerror = () => resolve(false);
            img.src = url;
        });
    },

    /**
     * 更新localStorage存储（支持对象合并/覆盖）
     * @param {string} storageName - 存储名称（命名空间）
     * @param {object} updates - 要更新的数据（支持深层路径如 "bootstrap-switch.showPopularSites"）
     * @param {boolean} [merge] - 是否合并到现有数据（false 则完全覆盖）
     * @returns {boolean} 是否更新成功
     */
    updateLocalStorage(storageName, updates, merge = true) {
        // 参数验证
        if (typeof storageName !== 'string' || !storageName.trim()) {
            console.error('storageName 必须是非空字符串');
            return false;
        }
        if (!updates || typeof updates !== 'object' || Array.isArray(updates)) {
            console.error('updates 必须是普通对象');
            return false;
        }

        try {
            // 获取现有数据
            const existingData = JSON.parse(localStorage.getItem(storageName)) || {};
            // 合并或覆盖数据
            const newData = merge
                ? AppUtils.deepMerge(existingData, updates)
                : updates;
            // 写入存储
            localStorage.setItem(storageName, JSON.stringify(newData));
            return true;
        } catch (error) {
            console.error(`更新 localStorage 失败 (${storageName}) - `, error);
            return false;
        }
    },

    /**
     * 深层合并对象（支持嵌套结构）
     * @param {object} target 目标对象
     * @param {object} source 源对象
     * @returns {object} 合并后的新对象
     */
    deepMerge(target, source) {
        const result = {...target};
        for (const key in source) {
            if (Object.prototype.hasOwnProperty.call(source, key)) {
                if (
                    typeof source[key] === 'object' &&
                    source[key] !== null &&
                    !Array.isArray(source[key]) &&
                    target[key]
                ) {
                    // 递归合并嵌套对象
                    result[key] = AppUtils.deepMerge(target[key], source[key]);
                } else {
                    // 直接赋值
                    result[key] = source[key];
                }
            }
        }
        return result;
    },
    /**
     * 设置背景图片
     * @param {string} url - 背景图片URL
     * @param {boolean} empty - 是否清空内容背景
     */
    setBgImage(url, empty = false) {
        $('.wallpaper').css({
                'background-image': `url(${url})`,
                'background-size': 'cover',
                'background-position': 'center',
                'background-attachment': 'fixed'
            });
        if (empty) {
            // 清空内容背景
            $('.page-container .grid-bg').css({
                'background-image': '',
            });
        }
    },
    /**
     * 生成文件哈希
     * @param {File} file - 文件对象
     * @returns {Promise<string>} 文件哈希值
     */
    async getFileHash(file) {
        const buffer = await file.arrayBuffer();
        const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
        return Array.from(new Uint8Array(hashBuffer))
            .map(b => b.toString(16).padStart(2, '0'))
            .join('');
    },
    /**
     * 获取背景图片 URL 列表
     * @param {boolean} reverse - 是否反转顺序
     * @returns {Promise<string[]>} 背景图片 URL 数组
     */
    getBgUrls(reverse = false) {
        return window.__cacheManager.getConfig('image', 'backgroundCollection').then(data => {
                const urls = data?.urls || [];
                return reverse ? urls.slice().reverse() : urls;
            });
    },
    /**
     * 获取当前背景图片
     * @returns {Promise<string>} 背景图片 URL 数组
     */
    getCurrentBgUrl() {
        return window.__cacheManager.getConfig('image', 'currentBackground').then(data => {
                return data?.url || '';
            });
    },
    /**
     * 从本地存储加载设置
     * @returns {object} 加载的设置对象
     */
    getUserSettings() {
        return  JSON.parse(localStorage.getItem('userSettings'));
    },
    /**
     * 读取文件 URL
     * @param file - 文件对象
     * @returns {Promise<unknown>} 文件 URL
     */
    readFileAsDataURL(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (event) => resolve(event.target.result);
            reader.onerror = reject;
            reader.readAsDataURL(file);
        });
    },
    /**
     * 截断字符串
     * @param str 要截断的字符串
     * @param maxLength 最大长度
     * @returns {string} 截断后的字符串
     */
    truncateString(str, maxLength) {
        if (str.length > maxLength) {
        return `${str.slice(0, maxLength)  }...`;
        }
        return str;
    },
    /**
     * 提取图片 URL
     * @param url 图片样式
     * @returns {string} 提取后的 URL
     */
    extractBase64Url(url) {
        return url.replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
    },
    /**
     * 平滑滚动到目标元素
     * @param {jQuery.Event} e - 点击事件对象
     * @param {jQuery} [triggerEl] - 可选的触发元素（非事件调用时传入）
     */
    handleScroll(e, triggerEl = null) {
         // 如果是事件触发，阻止默认行为并获取触发元素
        if (e) {
            e.preventDefault();
            triggerEl = $(this);
        }

        // 使用 requestAnimationFrame 优化动画性能
        const scrollTo = () => {
            const target = $(triggerEl.attr('href'));
            if (!target.length) return;

            const headerHeight = $('.page-header').outerHeight() || 0;
            // 偏移量，确保滚动到目标元素上方时不会被遮挡，数值越小容易被遮挡
            const offset = 20;
            const top = target.offset().top - headerHeight - offset;

            // 使用 CSSOM 平滑滚动（性能优于 jQuery.animate）
            window.scrollTo({
                top,
                behavior: 'smooth'
            });

            // 手动更新URL（避免页面跳动）
            history.replaceState(null, null, triggerEl.attr('href'));

        };

        requestAnimationFrame(scrollTo);
    },
    /**
    * 将 RGB 颜色值转换为十六进制
    * @param {string} rgb - RGB 颜色值，如 "rgb(255, 255, 255)"
    * @returns {string} 十六进制颜色值，如 "#ffffff"
    */
    rgbToHex(rgb) {
        // 提取 RGB 值
        const result = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i.exec(rgb);
        if (!result) return rgb; // 如果不是rgb格式，返回原值

        // 将每个分量转换为16进制
        const r = Number.parseInt(result[1]).toString(16).padStart(2, '0');
        const g = Number.parseInt(result[2]).toString(16).padStart(2, '0');
        const b = Number.parseInt(result[3]).toString(16).padStart(2, '0');

        return `#${r}${g}${b}`;
    },
    /**
     * 刷新表格并保持排序和主题状态
     * @param {string} tableSelector 表格选择器（如'#new-site-table'）
     * @param {string} sortField 要排序的字段名（如'update_time'）
     * @param {string} sortOrder 排序方向（'asc'或'desc'）
     */
    refreshTableWithSort(tableSelector, sortField, sortOrder) {
        // 参数校验
        if (!tableSelector || !sortField) {
            console.error('refreshTableWithSort: 缺少必要参数');
            return;
        }

        const $table = $(tableSelector);
        if ($table.length === 0) {
            console.error('refreshTableWithSort: 表格元素未找到');
            return;
        }

        // 保存当前主题
        const currentTheme = $table.hasClass('table-dark') ? 'dark' : 'light';

        try {
            // 刷新表格
            $table.bootstrapTable('refresh');

            // 通过 refreshOptions 排序
            $table.bootstrapTable('refreshOptions', {
                sortName: sortField,
                sortOrder: sortOrder || 'desc' // 默认降序
            });
        } catch (e) {
            // 降级方案：手动排序数据
            try {
                const data = $table.bootstrapTable('getData');
                if (data && data.length) {
                    const modifier = sortOrder === 'asc' ? 1 : -1;
                    data.sort((a, b) => (a[sortField] - b[sortField]) * modifier);
                    $table.bootstrapTable('load', data);
                }
            } catch (e) {
                console.error('refreshTableWithSort: 排序失败', e);
            }
        }

        // 恢复主题
        $table.removeClass('table-dark table-light')
            .addClass(currentTheme === 'dark' ? 'table-dark' : 'table-light');

        // 更新排序头样式
        $(`${tableSelector} th[data-field="${sortField}"]`).attr('data-order', sortOrder || 'desc')
    }
}
    // 自动挂载到全局，用于在生产环境中保护 JavaScript 文件中所有函数不被 Tree Shaking 移除
    window.AppUtils = AppUtils;
})(window.jQuery);